using System;
using System.Collections.Generic;
using System.Xml;
using System.Data;
using System.Diagnostics;
using Adoor.Expression;
using Adoor.Expression.AST;
using Adoor.Object;
using Adoor.Object.Entity;
using Adoor.Relational;
using Adoor.Data;  // For SmartKey
using Adoor.Object.Domain;

namespace Adoor.Object.Relational
{

    // guid stored as string for database compatibility
    // "00000000-0000-0000-0000-000000000000"
    public class GuidKeyMapping<TEntity> : CustomKeyMapping<TEntity>
    {
        public GuidKeyMapping(Adoor.Object.Domain.EntityData factory, XmlNode metadata)
            : base(factory, metadata)
        {
            if (this.DataColumns.Length != 1)
                throw new ArgumentException("columns.Length != -1", "key");
            foreach (DataColumn dc in DataColumns)
            {
                dc.DataType = typeof(string);
                dc.MaxLength = 36;
            }
        }

        public override string GetOid(KeyValue key)
        {
            if (key.Values.Length != 1)
                throw new ArgumentException("key.Length != 1", "key");
            return base.GetOid(key);
        }

        public override KeyValue ExtractKey(string oid)
        {
            KeyValue result = base.ExtractKey(oid);
            if (result.Values.Length != 1)
                throw new ArgumentException("result.Length != 1", "result");
            return result;
        }

        //		public override EntityData GetActualEntityData2(string oid)
        //		{
        //			return this.EntityData.GetActualEntityData(oid);  // polymorphism is allowed in this case, ask the EntityData for the actual entity data
        //		}
    }


    //	GUIDs consist of random numbers grouped into several sections: timestamp, clock sequence and node.
    //	The different sections for the GUID 8743428c-ef91-4d05-9e7c-4a2e856e813a are shown in the following table.
    //
    //	8743428c: Low field of the timestamp 
    //	ef91: Middle field of the timestamp 
    //	4d05: High field of the timestamp with multiplexed version number 
    //	9e: High field of the clock sequence with multiplexed variant type 
    //	7c: Low field of the clock sequence 
    //	4a2e856e813a: Spatially unique node identifier 

    //  see http://www.webdav.org/specs/draft-leach-uuids-guids-01.txt
    //
    //  In this factory, we generate a new GUID and replace its spatially unique node identifier part by the class id
    public class GuidKeyFactory<TEntity> : GuidKeyMapping<TEntity>, IOidFactory<TEntity>
    {
        //protected static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public GuidKeyFactory(Adoor.Object.Domain.EntityData entityData, XmlNode metadata)
            : base(entityData, metadata)
        {
            if (this.EntityData.ClassId == "")
                throw new Exception("attribute @classId not found for type " + this.EntityData.EntityType.Name);

            this.suffix = this.EntityData.ClassId.PadLeft(12, '0');

            AdoorLoger.Debug(" registering guidKeyFactory to parents " + entityData.EntityType.Name);
            

            Adoor.Object.Domain.EntityData parentData = this.EntityData.ParentData;
            while (parentData != null)
            {
                AdoorLoger.Debug("\tadding to " + parentData.EntityType.Name);
                

                parentData.PolymorphismManager.AddOidData(this);
                parentData = parentData.ParentData;
            }

            if (this.EntityData.PolymorphismManager != null)
                this.EntityData.PolymorphismManager.AddOidData(this);
        }

        public IOidData OidData
        {
            get { return this; }
        }

        public object Discriminator
        {
            get { return this.suffix; }
        }

        public void CreateKey(IEntityState state)
        {
            ((IRelationalEntityState)state)[this.PrimaryKey] = CreateNewKey();
        }

        public string CreateNewKey()
        {
            string guid = Guid.NewGuid().ToString();
            return string.Concat(guid.Substring(0, guid.Length - 12), this.suffix);
        }

        public string CreateKeyFromCle(long cle)
        {
            string guid = cle.ToString("00000000-0000-0000-0000-");
            return string.Concat(guid, this.suffix);
        }

        public long GetCleFromOID(string oid)
        {
            oid = oid.Substring(0, 23).Replace("-", "");
            foreach (char c in oid)
            {
                if (Char.IsLetter(c))
                    return -1;
            }
            return long.Parse(oid);
        }

        public override Adoor.Expression.AST.SelectExpression[] GetKeySelectExpression(string correlation)
        {
            ExpressionName[] names = GetKeyColumns(correlation);
            SelectExpression[] result = new SelectExpression[names.Length];
            for (int i = 0; i < result.Length; i++)
            {
                ExpressionFunction ef = new ExpressionFunction("CAST", names[i], new ExpressionPrimary(ScannerToken.String, "varchar(36)"));
                ExpressionFunction ef2 = new ExpressionFunction("LOWER", ef);
                result[i] = new SelectExpression(ef2, names[i].Value);
            }
            return result;
        }

        private string suffix;
    }
}
